{
module Main where

import Char (isAlpha, isDigit, isSpace)
import Scanner
}

%name parseGrammar 
%tokentype { Token }
%error { parseError }
%monad { E } { thenE } { returnE } 

--------------------------------------------------------------------------------
-- Token declarations
--------------------------------------------------------------------------------
 
%token
      "."                             { TokenPoint }
      "("                             { TokenOB }
      ")"                             { TokenCB }
      "{"                             { TokenOCurlyB }
      "}"                             { TokenCCurlyB }
      "for"                           { TokenFor }
      "file"                          { TokenFile }
      "filepool"                      { TokenFilePool }
      "root"                          { TokenRoot }
      "in"                            { TokenIn }
      "name"                          { TokenName }
      "filepoolsof"                   { TokenFilePoolsOf}
      "filesof"                       { TokenFilesOf }
      "exists"                        { TokenExists }
      "release"                       { TokenRelease }
      "delete"                        { TokenDelete } 
      "by"                            { TokenBy }
      "::"                            { TokenDoubleSemicolon }
      "iterator"                      { TokenIterator }
      "of"                            { TokenOf }
      "startingat"                    { TokenStartingAt }
      "norelease"                     { TokenNoRelease}
      "released"                      { TokenReleased }
      "hasdata"                       { TokenHasData }
      "data"                          { TokenData }
      "next"                          { TokenNext }
      "<-"                            { TokenWrite }
      "writing"                       { TokenWriting }
      "to"                            { TokenTo }
      "boolean"                       { TokenBooleanType }
      "byte"                          { TokenByteType }
      "short"                         { TokenShortType }
      "int"                           { TokenIntType }
      "long"                          { TokenLongType }
      "float"                         { TokenFloatType }
      "double"                        { TokenDoubleType }
      "["                             { TokenOSquareBracket }
      "]"                             { TokenCSquareBracket } 
      "size"                          { TokenSize }
      "rec"                           { TokenRecord }
      ":"                             { TokenSemicolon }
      "list"                          { TokenList }
      "+="                            { TokenAppend }
      "object"                        { TokenObject }
      "method"                        { TokenMethod }
      "field"                         { TokenField } 
      "comparator"                    { TokenComparator }
      "<"                             { TokenOAngleB }
      ">"                             { TokenCAngleB }
      "="                             { TokenEquals }
      int                             { TokenInt $$ }
      ident                           { TokenIdent $$ }
      stringLiteral                   { TokenStringLiteral $$ }
%%

--------------------------------------------------------------------------------
-- Grammar rules
--------------------------------------------------------------------------------

Statement : Type ident                                                     { VarDeclaration $2 $1 }
  | "writing" ident "to" ident "{" Statement "}"                           { Writing (DataType $2) $4 $5 }
  | "for" "(" ident "::" ident "by" ident "(" int ")" "{" Statement "}"    { Iterate $3 (FileByIterator $5 $7 $9) $12 }   
  | "sort" "(" ident "," ident "," ident ")"                               { Sort $3 (DataType $5) (Comparator $7) }  
  | "file" ident                                                           { FileDeclaration $2}  
  | ident "<-" ident                                                       { WriteToFile $1 $3} 
  | ident "+=" ident                                                       { ListAppend }
  | "break"                                                                { Break }

{
--------------------------------------------------------------------------------
-- Data types
-------------------------------------------------------------------------------
 
data Method 
  = Method String [Parameter] Statement

data Parameter 
  = Parameter String Type

data Type 
  = FileType 
  | IntType 
  | ListType
  | DataType String

data Statement 
  = Block [Statement]
  | VarDeclaration String Type
  | Iterate String Iterator String Statement
  | Sort String Type Comparator
  | FileDeclaration String
  | WriteToFile String String
  | ListAppend String String
  | Assignment String Expression
  | Writing Type String Statement
  | DoWhile Statement
  | If Expression Statement Statement
  | Break
  | Next String
  | Release String
  | Return Expression

data Comparator 
  = Comparator String 

data Iterator 
  = FileByIterator String Int Type
  | FileIterator String Type
  | ListIterator String 

data Expression 
  = IteratorExpression Iterator 
  | VariableReference String
  | Null 
  | Equals Expression Expression 
  | NotEquals Expression Expression 
  | Greater Expression Expression  
  | MethodCall String [String]
  | Or Expression Expression 
  | Not Expression 
  | HasData Iterator
  | Released Iterator
  | Data Iterator

--------------------------------------------------------------------------------
-- Monad for error reporting
--------------------------------------------------------------------------------

data E a = ParseOk a
  | ParseFailed String
  deriving Show

thenE :: E a -> (a -> E b) -> E b
m `thenE` k =
   case m of
       ParseOk a -> k a
       ParseFailed e -> ParseFailed e

returnE :: a -> E a
returnE a = ParseOk a

failE :: String -> E a
failE err = ParseFailed err

catchE :: E a -> (String -> E a) -> E a
catchE m k =
   case m of
      ParseOk a -> ParseOk a
      ParseFailed e -> k e

parseError :: [Token] -> E a
parseError tokens = failE ("Parse error on token "  ++ (show (head tokens)))

--------------------------------------------------------------------------------

main = do
  inStr <- getContents
  let parseTree = parseGrammar (alexScanTokens inStr)
  putStrLn(show(parseTree))
}

